package org.tinygroup.lucene;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;

public class LuceneDealer {
	private static Logger logger = LoggerFactory.getLogger(LuceneDealer.class);
	private static final int DEFAULT_MAX = 100;
	Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47);
	IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_47, analyzer);
	private String indexPath;
	private boolean create;
	IndexWriter writer;

	public LuceneDealer(String indexPath, String docsPath) {
		setIndexPath(indexPath);
		File file = new File(docsPath);
		List<File> files = new ArrayList<File>();
		collectFiles(file, files);
		addFile(files);

	}

	public LuceneDealer(String indexPath, List<File> files) {
		setIndexPath(indexPath);
		addFile(files);

	}

	private void collectFiles(File file, List<File> files) {
		if (!file.isDirectory()) {
			files.add(file);
		}
		if (file.listFiles() == null) {
			return;
		}
		for (File f : file.listFiles()) {
			collectFiles(f, files);
		}
	}

	public boolean isCreate() {
		return create;
	}

	public void setCreate(boolean create) {
		this.create = create;
	}

	public String getIndexPath() {
		return indexPath;
	}

	private void setIndexPath(String indexPath) {
		this.indexPath = indexPath;
		logger.logMessage(LogLevel.INFO, "索引文件存放目录:{}", indexPath);
	}

	private IndexWriter getWriter() throws IOException {
		if (writer == null) {
			File indexFile = new File(indexPath);
			if (indexFile.exists()) {
				indexFile.mkdirs();
			}
			Directory dir = FSDirectory.open(indexFile);
			writer = new IndexWriter(dir, iwc);
		}
		return writer;

	}

	private void addFile(List<File> files) {
		try {
			writer = getWriter();
			if (create) {
				iwc.setOpenMode(OpenMode.CREATE);
			} else {
				iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
			}
			for (File file : files) {
				try {
					addFile(file);
				} catch (IOException e) {
					logger.errorMessage("读取文件:{}时出错", e, file.getAbsolutePath());
				}
			}
			writer.close();
		} catch (IOException e1) {
			logger.errorMessage("处理IndexWriter时出错", e1);
			throw new RuntimeException(e1);
		}

	}

	private void addFile(File file) throws IOException {
		logger.logMessage(LogLevel.INFO, "开始读取文件:{}", file.getAbsolutePath());
		FileInputStream fis;
		try {
			fis = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			logger.errorMessage("文件:{}未找到", e, file.getAbsolutePath());
			return;
		}
		try {
			Document doc = new Document();
			Field pathField = new StringField("path", file.getPath(),
					Field.Store.YES);
			doc.add(pathField);
			doc.add(new LongField("modified", file.lastModified(),
					Field.Store.NO));
			doc.add(new TextField("contents", new BufferedReader(
					new InputStreamReader(fis, "UTF-8"))));
			if (writer.getConfig().getOpenMode() == OpenMode.CREATE) {
				writer.addDocument(doc);
			} else {
				writer.updateDocument(new Term("path", file.getPath()), doc);
			}
		} finally {
			fis.close();
		}
		logger.logMessage(LogLevel.INFO, "读取文件:{}结束", file.getAbsolutePath());
	}

	public List<String> find(String field, String queryString)
			throws IOException, UnsupportedEncodingException,
			FileNotFoundException, ParseException {
		logger.logMessage(LogLevel.INFO, "开始在{}中查找{}", field, queryString);
		IndexReader reader = DirectoryReader.open(FSDirectory.open(new File(
				indexPath)));
		IndexSearcher searcher = new IndexSearcher(reader);
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47);
		QueryParser parser = new QueryParser(Version.LUCENE_47, field, analyzer);
		Query query = parser.parse(queryString);
		ScoreDoc[] result = doPagingSearch(searcher, query);
		List<String> resultPaths = new ArrayList<String>();
		for (int i = 0; i < result.length; i++) {
			Document doc = searcher.doc(result[i].doc);
			String path = doc.get("path");
			resultPaths.add(path);
		}
		reader.close();
		logger.logMessage(LogLevel.INFO, "在{}中查找{}完成，共找到文件数:{}", field,
				queryString, resultPaths.size());
		return resultPaths;
	}

	private ScoreDoc[] doPagingSearch(IndexSearcher searcher, Query query)
			throws IOException {

		TopDocs results = searcher.search(query, DEFAULT_MAX);
		ScoreDoc[] hits = results.scoreDocs;
		int numTotalHits = results.totalHits;
		// System.out.println(numTotalHits + " total matching documents");
		int end = Math.min(numTotalHits, DEFAULT_MAX);
		if (end > hits.length) {
			hits = searcher.search(query, numTotalHits).scoreDocs;
		}
		return hits;

		// end = Math.min(hits.length, start + DEFAULT_MAX);
		// for (int i = start; i < end; i++) {
		//
		// Document doc = searcher.doc(hits[i].doc);
		// String path = doc.get("path");
		// if (path != null) {
		// System.out.println((i + 1) + ". " + path);
		// String title = doc.get("title");
		// if (title != null) {
		// System.out.println("   Title: " + doc.get("title"));
		// }
		// } else {
		// System.out
		// .println((i + 1) + ". " + "No path for this document");
		// }
		//
		// }
	}
}
